{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5c0122e65c053f38",
   "metadata": {},
   "source": [
    "# Hosting MCP Server on Amazon Bedrock AgentCore Runtime\n",
    "\n",
    "## Overview\n",
    "\n",
    "In this tutorial we will learn how to host MCP (Model Context Protocol) servers on Amazon Bedrock AgentCore Runtime. We will use the Amazon Bedrock AgentCore Python SDK to wrap MCP tools as an MCP server compatible with Amazon Bedrock AgentCore.\n",
    "\n",
    "The Amazon Bedrock AgentCore Python SDK handles the MCP server implementation details so you can focus on your tools' core functionality. It transforms your code into the AgentCore standardized MCP protocol contracts for direct communication.\n",
    "\n",
    "### Tutorial Details\n",
    "\n",
    "| Information         | Details                                                   |\n",
    "|:--------------------|:----------------------------------------------------------|\n",
    "| Tutorial type       | Hosting Tools                                             |\n",
    "| Tool type           | MCP server                                                |\n",
    "| Tutorial components | Hosting MCP server on AgentCore Runtime                  |\n",
    "| Tutorial vertical   | Cross-vertical                                            |\n",
    "| Example complexity  | Easy                                                      |\n",
    "| SDK used            | Amazon BedrockAgentCore Python SDK and MCP               |\n",
    "\n",
    "### Tutorial Architecture\n",
    "\n",
    "In this tutorial we will describe how to deploy an MCP server to AgentCore runtime.\n",
    "\n",
    "For demonstration purposes, we will use a simple MCP server with 3 tools: `add_numbers`, `multiply_numbers` and `greet_user`\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/hosting_mcp_server.png\" width=\"60%\"/>\n",
    "</div>\n",
    "\n",
    "### Tutorial Key Features\n",
    "\n",
    "* Creating MCP servers with custom tools\n",
    "* Testing MCP servers locally\n",
    "* Hosting MCP servers on Amazon Bedrock AgentCore Runtime\n",
    "* Invoking deployed MCP servers with authentication\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a676f58ecf52b42",
   "metadata": {},
   "source": [
    "## Prerequisites\n",
    "\n",
    "To execute this tutorial you will need:\n",
    "* Python 3.10+\n",
    "* AWS credentials configured\n",
    "* Amazon Bedrock AgentCore SDK\n",
    "* MCP (Model Context Protocol) library\n",
    "* Docker running"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "initial_id",
   "metadata": {
    "jupyter": {
     "is_executing": true
    }
   },
   "outputs": [],
   "source": [
    "#!uv add -r requirements.txt --active"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca924a7a2731e26f",
   "metadata": {},
   "source": [
    "## Understanding MCP (Model Context Protocol)\n",
    "\n",
    "MCP is a protocol that allows AI models to securely access external data and tools. Key concepts:\n",
    "\n",
    "* **Tools**: Functions that the AI can call to perform actions\n",
    "* **Streamable HTTP**: Transport protocol used by AgentCore Runtime\n",
    "* **Session Isolation**: Each client gets isolated sessions via `Mcp-Session-Id` header\n",
    "* **Stateless Operation**: Servers must support stateless operation for scalability\n",
    "\n",
    "AgentCore Runtime expects MCP servers to be hosted on `0.0.0.0:8000/mcp` as the default path.\n",
    "\n",
    "### Project Structure\n",
    "\n",
    "Let's set up our project with the proper structure:\n",
    "\n",
    "```\n",
    "mcp_server_project/\n",
    "├── mcp_server.py              # Main MCP server code\n",
    "├── my_mcp_client.py          # Local testing client\n",
    "├── my_mcp_client_remote.py   # Remote testing client\n",
    "├── requirements.txt          # Dependencies\n",
    "└── __init__.py              # Python package marker\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step2_create_server",
   "metadata": {},
   "source": [
    "## Creating MCP Server\n",
    "\n",
    "Now let's create our MCP server with three simple tools. The server uses FastMCP with `stateless_http=True` which is required for AgentCore Runtime compatibility."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7d386ab54e85e63",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile mcp_server.py\n",
    "from mcp.server.fastmcp import FastMCP\n",
    "from starlette.responses import JSONResponse\n",
    "\n",
    "mcp = FastMCP(host=\"0.0.0.0\", stateless_http=True)\n",
    "\n",
    "@mcp.tool()\n",
    "def add_numbers(a: int, b: int) -> int:\n",
    "    \"\"\"Add two numbers together\"\"\"\n",
    "    return a + b\n",
    "\n",
    "@mcp.tool()\n",
    "def multiply_numbers(a: int, b: int) -> int:\n",
    "    \"\"\"Multiply two numbers together\"\"\"\n",
    "    return a * b\n",
    "\n",
    "@mcp.tool()\n",
    "def greet_user(name: str) -> str:\n",
    "    \"\"\"Greet a user by name\"\"\"\n",
    "    return f\"Hello, {name}! Nice to meet you.\"\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    mcp.run(transport=\"streamable-http\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "understand_code",
   "metadata": {},
   "source": [
    "### What This Code Does\n",
    "\n",
    "* **FastMCP**: Creates an MCP server that can host your tools\n",
    "* **@mcp.tool()**: Decorator that turns your Python functions into MCP tools\n",
    "* **stateless_http=True**: Required for AgentCore Runtime compatibility\n",
    "* **Tools**: Three simple tools demonstrating different types of operations"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step3_local_client",
   "metadata": {},
   "source": [
    "## Creating Local Testing Client\n",
    "\n",
    "Before deploying to AgentCore Runtime, let's create a client to test our MCP server locally:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1226d59e6b56c96",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile my_mcp_client.py\n",
    "import asyncio\n",
    "\n",
    "from mcp import ClientSession\n",
    "from mcp.client.streamable_http import streamablehttp_client\n",
    "\n",
    "async def main():\n",
    "    mcp_url = \"http://localhost:8000/mcp\"\n",
    "    headers = {}\n",
    "\n",
    "    async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (\n",
    "        read_stream,\n",
    "        write_stream,\n",
    "        _,\n",
    "    ):\n",
    "        async with ClientSession(read_stream, write_stream) as session:\n",
    "            await session.initialize()\n",
    "            tool_result = await session.list_tools()\n",
    "            print(\"Available tools:\")\n",
    "            for tool in tool_result.tools:\n",
    "                print(f\"  - {tool.name}: {tool.description}\")\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    asyncio.run(main())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "test_locally_instructions",
   "metadata": {},
   "source": [
    "### Testing Locally\n",
    "\n",
    "To test your MCP server locally:\n",
    "\n",
    "1. **Terminal 1**: Start the MCP server\n",
    "   ```bash\n",
    "   python mcp_server.py\n",
    "   ```\n",
    "   \n",
    "2. **Terminal 2**: Run the test client\n",
    "   ```bash\n",
    "   python my_mcp_client.py\n",
    "   ```\n",
    "\n",
    "You should see your three tools listed in the output."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step4_cognito_setup",
   "metadata": {},
   "source": [
    "## Setting up Amazon Cognito for Authentication\n",
    "\n",
    "AgentCore Runtime requires authentication. We'll use Amazon Cognito to provide JWT tokens for accessing our deployed MCP server."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "import_utils",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "import os\n",
    "\n",
    "# Get the current notebook's directory\n",
    "current_dir = os.path.dirname(os.path.abspath('__file__' if '__file__' in globals() else '.'))\n",
    "\n",
    "utils_dir = os.path.join(current_dir, '..')\n",
    "utils_dir = os.path.abspath(utils_dir)\n",
    "\n",
    "# Add to sys.path\n",
    "sys.path.insert(0, utils_dir)\n",
    "print(\"sys.path[0]:\", sys.path[0])\n",
    "\n",
    "from utils import create_agentcore_role, setup_cognito_user_pool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "setup_cognito",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Setting up Amazon Cognito user pool...\")\n",
    "cognito_config = setup_cognito_user_pool()\n",
    "print(\"Cognito setup completed ✓\")\n",
    "print(f\"User Pool ID: {cognito_config.get('user_pool_id', 'N/A')}\")\n",
    "print(f\"Client ID: {cognito_config.get('client_id', 'N/A')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step5_iam_role",
   "metadata": {},
   "source": [
    "## Step 5: Create IAM Execution Role\n",
    "\n",
    "Before starting, let's create an IAM role for our AgentCore Runtime. This role provides the necessary permissions for the runtime to operate."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54dd2fdf-985c-4a70-8b87-071783a209de",
   "metadata": {},
   "outputs": [],
   "source": [
    "tool_name = \"mcp_server_ac\"\n",
    "print(f\"Creating IAM role for {tool_name}...\")\n",
    "agentcore_iam_role = create_agentcore_role(agent_name=tool_name)\n",
    "print(f\"IAM role created ✓\")\n",
    "print(f\"Role ARN: {agentcore_iam_role['Role']['Arn']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step6_configure_deployment",
   "metadata": {},
   "source": [
    "## Configuring AgentCore Runtime Deployment\n",
    "\n",
    "Next we will use our starter toolkit to configure the AgentCore Runtime deployment with an entrypoint, the execution role we just created and a requirements file. We will also configure the starter kit to auto create the Amazon ECR repository on launch.\n",
    "\n",
    "During the configure step, your docker file will be generated based on your application code\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/configure.png\" width=\"60%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "configure_runtime",
   "metadata": {},
   "outputs": [],
   "source": [
    "from bedrock_agentcore_starter_toolkit import Runtime\n",
    "from boto3.session import Session\n",
    "import time\n",
    "\n",
    "boto_session = Session()\n",
    "region = boto_session.region_name\n",
    "print(f\"Using AWS region: {region}\")\n",
    "\n",
    "required_files = ['mcp_server.py', 'requirements.txt']\n",
    "for file in required_files:\n",
    "    if not os.path.exists(file):\n",
    "        raise FileNotFoundError(f\"Required file {file} not found\")\n",
    "print(\"All required files found ✓\")\n",
    "\n",
    "agentcore_runtime = Runtime()\n",
    "\n",
    "auth_config = {\n",
    "    \"customJWTAuthorizer\": {\n",
    "        \"allowedClients\": [\n",
    "            cognito_config['client_id']\n",
    "        ],\n",
    "        \"discoveryUrl\": cognito_config['discovery_url'],\n",
    "    }\n",
    "}\n",
    "\n",
    "print(\"Configuring AgentCore Runtime...\")\n",
    "response = agentcore_runtime.configure(\n",
    "    entrypoint=\"mcp_server.py\",\n",
    "    execution_role=agentcore_iam_role['Role']['Arn'],\n",
    "    auto_create_ecr=True,\n",
    "    requirements_file=\"requirements.txt\",\n",
    "    region=region,\n",
    "    authorizer_configuration=auth_config,\n",
    "    protocol=\"MCP\",\n",
    "    agent_name=tool_name\n",
    ")\n",
    "print(\"Configuration completed ✓\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step7_launch",
   "metadata": {},
   "source": [
    "## Launching MCP Server to AgentCore Runtime\n",
    "\n",
    "Now that we've got a docker file, let's launch the MCP server to the AgentCore Runtime. This will create the Amazon ECR repository and the AgentCore Runtime\n",
    "\n",
    "<div style=\"text-align:left\">\n",
    "    <img src=\"images/launch.png\" width=\"85%\"/>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17a32ab8-7701-4900-8055-e24364bdf35c",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Launching MCP server to AgentCore Runtime...\")\n",
    "print(\"This may take several minutes...\")\n",
    "launch_result = agentcore_runtime.launch()\n",
    "print(\"Launch completed ✓\")\n",
    "print(f\"Agent ARN: {launch_result.agent_arn}\")\n",
    "print(f\"Agent ID: {launch_result.agent_id}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step8_check_status",
   "metadata": {},
   "source": [
    "## Checking AgentCore Runtime Status\n",
    "\n",
    "Now that we've deployed the AgentCore Runtime, let's check for its deployment status and wait for it to be ready:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "afa6ac09-9adb-4846-9fc1-4d12aeb74853",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Checking AgentCore Runtime status...\")\n",
    "status_response = agentcore_runtime.status()\n",
    "status = status_response.endpoint['status']\n",
    "print(f\"Initial status: {status}\")\n",
    "\n",
    "end_status = ['READY', 'CREATE_FAILED', 'DELETE_FAILED', 'UPDATE_FAILED']\n",
    "while status not in end_status:\n",
    "    print(f\"Status: {status} - waiting...\")\n",
    "    time.sleep(10)\n",
    "    status_response = agentcore_runtime.status()\n",
    "    status = status_response.endpoint['status']\n",
    "\n",
    "if status == 'READY':\n",
    "    print(\"✓ AgentCore Runtime is READY!\")\n",
    "else:\n",
    "    print(f\"⚠ AgentCore Runtime status: {status}\")\n",
    "    \n",
    "print(f\"Final status: {status}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step9_store_credentials",
   "metadata": {},
   "source": [
    "## Storing Configuration for Remote Access\n",
    "\n",
    "Before we can invoke our deployed MCP server, let's store the Agent ARN and Cognito configuration in AWS Systems Manager Parameter Store and AWS Secrets Manager for easy retrieval:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "store_config",
   "metadata": {},
   "outputs": [],
   "source": [
    "import boto3\n",
    "import json\n",
    "\n",
    "ssm_client = boto3.client('ssm', region_name=region)\n",
    "secrets_client = boto3.client('secretsmanager', region_name=region)\n",
    "\n",
    "try:\n",
    "    cognito_credentials_response = secrets_client.create_secret(\n",
    "        Name='mcp_server/cognito/credentials',\n",
    "        Description='Cognito credentials for MCP server',\n",
    "        SecretString=json.dumps(cognito_config)\n",
    "    )\n",
    "    print(\"✓ Cognito credentials stored in Secrets Manager\")\n",
    "except secrets_client.exceptions.ResourceExistsException:\n",
    "    secrets_client.update_secret(\n",
    "        SecretId='mcp_server/cognito/credentials',\n",
    "        SecretString=json.dumps(cognito_config)\n",
    "    )\n",
    "    print(\"✓ Cognito credentials updated in Secrets Manager\")\n",
    "\n",
    "agent_arn_response = ssm_client.put_parameter(\n",
    "    Name='/mcp_server/runtime/agent_arn',\n",
    "    Value=launch_result.agent_arn,\n",
    "    Type='String',\n",
    "    Description='Agent ARN for MCP server',\n",
    "    Overwrite=True\n",
    ")\n",
    "print(\"✓ Agent ARN stored in Parameter Store\")\n",
    "\n",
    "print(\"\\nConfiguration stored successfully!\")\n",
    "print(f\"Agent ARN: {launch_result.agent_arn}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step10_remote_client",
   "metadata": {},
   "source": [
    "## Creating Remote Testing Client\n",
    "\n",
    "Now let's create a client to test our deployed MCP server. This client will retrieve the necessary credentials from AWS and connect to the deployed server:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "create_remote_client",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile my_mcp_client_remote.py\n",
    "import asyncio\n",
    "import boto3\n",
    "import json\n",
    "import sys\n",
    "from boto3.session import Session\n",
    "\n",
    "from mcp import ClientSession\n",
    "from mcp.client.streamable_http import streamablehttp_client\n",
    "\n",
    "async def main():\n",
    "    boto_session = Session()\n",
    "    region = boto_session.region_name\n",
    "    \n",
    "    print(f\"Using AWS region: {region}\")\n",
    "    \n",
    "    try:\n",
    "        ssm_client = boto3.client('ssm', region_name=region)\n",
    "        agent_arn_response = ssm_client.get_parameter(Name='/mcp_server/runtime/agent_arn')\n",
    "        agent_arn = agent_arn_response['Parameter']['Value']\n",
    "        print(f\"Retrieved Agent ARN: {agent_arn}\")\n",
    "\n",
    "        secrets_client = boto3.client('secretsmanager', region_name=region)\n",
    "        response = secrets_client.get_secret_value(SecretId='mcp_server/cognito/credentials')\n",
    "        secret_value = response['SecretString']\n",
    "        parsed_secret = json.loads(secret_value)\n",
    "        bearer_token = parsed_secret['bearer_token']\n",
    "        print(\"✓ Retrieved bearer token from Secrets Manager\")\n",
    "        \n",
    "    except Exception as e:\n",
    "        print(f\"Error retrieving credentials: {e}\")\n",
    "        sys.exit(1)\n",
    "    \n",
    "    if not agent_arn or not bearer_token:\n",
    "        print(\"Error: AGENT_ARN or BEARER_TOKEN not retrieved properly\")\n",
    "        sys.exit(1)\n",
    "    \n",
    "    encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')\n",
    "    mcp_url = f\"https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT\"\n",
    "    headers = {\n",
    "        \"authorization\": f\"Bearer {bearer_token}\",\n",
    "        \"Content-Type\": \"application/json\"\n",
    "    }\n",
    "    \n",
    "    print(f\"\\nConnecting to: {mcp_url}\")\n",
    "    print(\"Headers configured ✓\")\n",
    "\n",
    "    try:\n",
    "        async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (\n",
    "            read_stream,\n",
    "            write_stream,\n",
    "            _,\n",
    "        ):\n",
    "            async with ClientSession(read_stream, write_stream) as session:\n",
    "                print(\"\\n🔄 Initializing MCP session...\")\n",
    "                await session.initialize()\n",
    "                print(\"✓ MCP session initialized\")\n",
    "                \n",
    "                print(\"\\n🔄 Listing available tools...\")\n",
    "                tool_result = await session.list_tools()\n",
    "                \n",
    "                print(\"\\n📋 Available MCP Tools:\")\n",
    "                print(\"=\" * 50)\n",
    "                for tool in tool_result.tools:\n",
    "                    print(f\"🔧 {tool.name}\")\n",
    "                    print(f\"   Description: {tool.description}\")\n",
    "                    if hasattr(tool, 'inputSchema') and tool.inputSchema:\n",
    "                        properties = tool.inputSchema.get('properties', {})\n",
    "                        if properties:\n",
    "                            print(f\"   Parameters: {list(properties.keys())}\")\n",
    "                    print()\n",
    "                \n",
    "                print(f\"✅ Successfully connected to MCP server!\")\n",
    "                print(f\"Found {len(tool_result.tools)} tools available.\")\n",
    "                \n",
    "    except Exception as e:\n",
    "        print(f\"❌ Error connecting to MCP server: {e}\")\n",
    "        sys.exit(1)\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    asyncio.run(main())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step11_test_remote",
   "metadata": {},
   "source": [
    "## Testing Your Deployed MCP Server\n",
    "\n",
    "Let's test our deployed MCP server using the remote client:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "test_remote_server",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Testing deployed MCP server...\")\n",
    "print(\"=\" * 50)\n",
    "!python my_mcp_client_remote.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "step12_invoke_tools",
   "metadata": {},
   "source": [
    "## Invoking MCP Tools Remotely\n",
    "\n",
    "Now let's create an enhanced client that not only lists tools but also invokes them to demonstrate the full MCP functionality:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "invoke_mcp_tools",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile invoke_mcp_tools.py\n",
    "import asyncio\n",
    "import boto3\n",
    "import json\n",
    "import sys\n",
    "from boto3.session import Session\n",
    "\n",
    "from mcp import ClientSession\n",
    "from mcp.client.streamable_http import streamablehttp_client\n",
    "\n",
    "async def main():\n",
    "    boto_session = Session()\n",
    "    region = boto_session.region_name\n",
    "    \n",
    "    print(f\"Using AWS region: {region}\")\n",
    "    \n",
    "    try:\n",
    "        ssm_client = boto3.client('ssm', region_name=region)\n",
    "        agent_arn_response = ssm_client.get_parameter(Name='/mcp_server/runtime/agent_arn')\n",
    "        agent_arn = agent_arn_response['Parameter']['Value']\n",
    "        print(f\"Retrieved Agent ARN: {agent_arn}\")\n",
    "\n",
    "        secrets_client = boto3.client('secretsmanager', region_name=region)\n",
    "        response = secrets_client.get_secret_value(SecretId='mcp_server/cognito/credentials')\n",
    "        secret_value = response['SecretString']\n",
    "        parsed_secret = json.loads(secret_value)\n",
    "        bearer_token = parsed_secret['bearer_token']\n",
    "        print(\"✓ Retrieved bearer token from Secrets Manager\")\n",
    "        \n",
    "    except Exception as e:\n",
    "        print(f\"Error retrieving credentials: {e}\")\n",
    "        sys.exit(1)\n",
    "    \n",
    "    encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')\n",
    "    mcp_url = f\"https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT\"\n",
    "    headers = {\n",
    "        \"authorization\": f\"Bearer {bearer_token}\",\n",
    "        \"Content-Type\": \"application/json\"\n",
    "    }\n",
    "    \n",
    "    print(f\"\\nConnecting to: {mcp_url}\")\n",
    "\n",
    "    try:\n",
    "        async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (\n",
    "            read_stream,\n",
    "            write_stream,\n",
    "            _,\n",
    "        ):\n",
    "            async with ClientSession(read_stream, write_stream) as session:\n",
    "                print(\"\\n🔄 Initializing MCP session...\")\n",
    "                await session.initialize()\n",
    "                print(\"✓ MCP session initialized\")\n",
    "                \n",
    "                print(\"\\n🔄 Listing available tools...\")\n",
    "                tool_result = await session.list_tools()\n",
    "                \n",
    "                print(\"\\n📋 Available MCP Tools:\")\n",
    "                print(\"=\" * 50)\n",
    "                for tool in tool_result.tools:\n",
    "                    print(f\"🔧 {tool.name}: {tool.description}\")\n",
    "                \n",
    "                print(\"\\n🧪 Testing MCP Tools:\")\n",
    "                print(\"=\" * 50)\n",
    "                \n",
    "                try:\n",
    "                    print(\"\\n➕ Testing add_numbers(5, 3)...\")\n",
    "                    add_result = await session.call_tool(\n",
    "                        name=\"add_numbers\",\n",
    "                        arguments={\"a\": 5, \"b\": 3}\n",
    "                    )\n",
    "                    print(f\"   Result: {add_result.content[0].text}\")\n",
    "                except Exception as e:\n",
    "                    print(f\"   Error: {e}\")\n",
    "                \n",
    "                try:\n",
    "                    print(\"\\n✖️  Testing multiply_numbers(4, 7)...\")\n",
    "                    multiply_result = await session.call_tool(\n",
    "                        name=\"multiply_numbers\",\n",
    "                        arguments={\"a\": 4, \"b\": 7}\n",
    "                    )\n",
    "                    print(f\"   Result: {multiply_result.content[0].text}\")\n",
    "                except Exception as e:\n",
    "                    print(f\"   Error: {e}\")\n",
    "                \n",
    "                try:\n",
    "                    print(\"\\n👋 Testing greet_user('Alice')...\")\n",
    "                    greet_result = await session.call_tool(\n",
    "                        name=\"greet_user\",\n",
    "                        arguments={\"name\": \"Alice\"}\n",
    "                    )\n",
    "                    print(f\"   Result: {greet_result.content[0].text}\")\n",
    "                except Exception as e:\n",
    "                    print(f\"   Error: {e}\")\n",
    "                \n",
    "                print(\"\\n✅ MCP tool testing completed!\")\n",
    "                \n",
    "    except Exception as e:\n",
    "        print(f\"❌ Error connecting to MCP server: {e}\")\n",
    "        sys.exit(1)\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    asyncio.run(main())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "test_tool_invocation",
   "metadata": {},
   "source": [
    "## Test Tool Invocation\n",
    "\n",
    "Let's test our MCP tools by actually invoking them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "run_tool_tests",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Testing MCP tool invocation...\")\n",
    "print(\"=\" * 50)\n",
    "!python invoke_mcp_tools.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "next_steps",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "\n",
    "Now that you have successfully deployed an MCP server to AgentCore Runtime, you can:\n",
    "\n",
    "1. **Add More Tools**: Extend your MCP server with additional tools\n",
    "2. **Custom Authentication**: Implement custom JWT authorizers\n",
    "3. **Integration**: Integrate with other AgentCore services"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cleanup_section",
   "metadata": {},
   "source": [
    "## Cleanup (Optional)\n",
    "\n",
    "If you want to clean up the resources created during this tutorial, run the following cells:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cleanup_resources",
   "metadata": {},
   "outputs": [],
   "source": [
    "import boto3\n",
    "\n",
    "print(\"🗑️  Starting cleanup process...\")\n",
    "\n",
    "agentcore_control_client = boto3.client('bedrock-agentcore-control', region_name=region)\n",
    "ecr_client = boto3.client('ecr', region_name=region)\n",
    "iam_client = boto3.client('iam')\n",
    "ssm_client = boto3.client('ssm', region_name=region)\n",
    "secrets_client = boto3.client('secretsmanager', region_name=region)\n",
    "\n",
    "try:\n",
    "    print(\"Deleting AgentCore Runtime...\")\n",
    "    runtime_delete_response = agentcore_control_client.delete_agent_runtime(\n",
    "        agentRuntimeId=launch_result.agent_id,\n",
    "    )\n",
    "    print(\"✓ AgentCore Runtime deletion initiated\")\n",
    "\n",
    "    print(\"Deleting ECR repository...\")\n",
    "    ecr_repo_name = launch_result.ecr_uri.split('/')[1]\n",
    "    ecr_client.delete_repository(\n",
    "        repositoryName=ecr_repo_name,\n",
    "        force=True\n",
    "    )\n",
    "    print(\"✓ ECR repository deleted\")\n",
    "\n",
    "    print(\"Deleting IAM role policies...\")\n",
    "    policies = iam_client.list_role_policies(\n",
    "        RoleName=agentcore_iam_role['Role']['RoleName'],\n",
    "        MaxItems=100\n",
    "    )\n",
    "\n",
    "    for policy_name in policies['PolicyNames']:\n",
    "        iam_client.delete_role_policy(\n",
    "            RoleName=agentcore_iam_role['Role']['RoleName'],\n",
    "            PolicyName=policy_name\n",
    "        )\n",
    "    \n",
    "    iam_client.delete_role(\n",
    "        RoleName=agentcore_iam_role['Role']['RoleName']\n",
    "    )\n",
    "    print(\"✓ IAM role deleted\")\n",
    "\n",
    "    try:\n",
    "        ssm_client.delete_parameter(Name='/mcp_server/runtime/agent_arn')\n",
    "        print(\"✓ Parameter Store parameter deleted\")\n",
    "    except ssm_client.exceptions.ParameterNotFound:\n",
    "        print(\"ℹ️  Parameter Store parameter not found\")\n",
    "\n",
    "    try:\n",
    "        secrets_client.delete_secret(\n",
    "            SecretId='mcp_server/cognito/credentials',\n",
    "            ForceDeleteWithoutRecovery=True\n",
    "        )\n",
    "        print(\"✓ Secrets Manager secret deleted\")\n",
    "    except secrets_client.exceptions.ResourceNotFoundException:\n",
    "        print(\"ℹ️  Secrets Manager secret not found\")\n",
    "\n",
    "    print(\"\\n✅ Cleanup completed successfully!\")\n",
    "    \n",
    "except Exception as e:\n",
    "    print(f\"❌ Error during cleanup: {e}\")\n",
    "    print(\"You may need to manually clean up some resources.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "congratulations",
   "metadata": {},
   "source": [
    "# 🎉 Congratulations!\n",
    "\n",
    "You have successfully:\n",
    "\n",
    "✅ **Created an MCP server** with custom tools  \n",
    "✅ **Tested locally** using MCP client  \n",
    "✅ **Set up authentication** with Amazon Cognito  \n",
    "✅ **Deployed to AWS** using AgentCore Runtime  \n",
    "✅ **Invoked remotely** with proper authentication  \n",
    "✅ **Learned MCP concepts** and best practices  \n",
    "\n",
    "Your MCP server is now running on Amazon Bedrock AgentCore Runtime and ready for production use!\n",
    "\n",
    "## Summary\n",
    "\n",
    "In this tutorial, you learned how to:\n",
    "- Build MCP servers using FastMCP\n",
    "- Configure stateless HTTP transport for AgentCore compatibility\n",
    "- Set up JWT authentication with Amazon Cognito\n",
    "- Deploy and manage MCP servers on AWS\n",
    "- Test both locally and remotely\n",
    "- Use MCP clients for tool invocation\n",
    "\n",
    "The deployed MCP server can now be integrated into larger AI applications and workflows!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
